/*******************************************************************************
* Copyright (c) 2008 Anyware Technologies and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Philippe Roland (Atos) - initial API and implementation
*******************************************************************************/
package org.eclipse.umlgen.reverse.java;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BlockComment;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.Comment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.LineComment;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.ActivityNode;
import org.eclipse.uml2.uml.ActivityParameterNode;
import org.eclipse.uml2.uml.AddVariableValueAction;
import org.eclipse.uml2.uml.BehavioralFeature;
import org.eclipse.uml2.uml.BehavioredClassifier;
import org.eclipse.uml2.uml.CallOperationAction;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.ControlFlow;
import org.eclipse.uml2.uml.DecisionNode;
import org.eclipse.uml2.uml.FinalNode;
import org.eclipse.uml2.uml.InitialNode;
import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.MergeNode;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.OpaqueAction;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Reception;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.Variable;
import org.eclipse.umlgen.reverse.java.logging.LogUtils;
/**
* Activity generation (nodes and control flow).
*/
public class JavaReverseCUVisitor extends ASTVisitor {
/** annotation. */
private static final String ANNOTATION_ACTIVITY = "@activity";
/** The uml package object. */
protected Package packageObject;
/** The current CU name. */
protected String currentCUName;
/** The current activity. */
protected Activity currentActivity;
/** The initial node. */
protected InitialNode initNode;
/** The final node. */
protected FinalNode finalNode;
/** The last statement. */
protected Statement lastStatement;
/** The last activity node. */
protected ActivityNode lastActivityNode;
// Map where the different object were saved
protected Map<Statement, ActivityNode> entryNodeMap;
protected Map<Statement, ActivityNode> exitNodeMap;
protected Map<Statement, ActivityNode> innerExitMap;
protected Map<Statement, ActivityNode> innerEntryMap;
// If/then/else processing
protected Map<Statement, ActivityNode> choiceMap;
protected Map<Statement, Statement> choiceThenFMap;
protected Map<Statement, Statement> choiceElseFMap;
protected Map<Statement, Statement> choiceElseMap;
protected Map<Statement, Statement> choiceThenMap;
protected Map<Statement, ActivityNode> choiceOutMap;
protected LiteralString lastGuard;
/** parent for loop and various blocks. */
protected List<Statement> parentStatements;
/** comments (added to next node). */
protected String nextComment = "";
protected boolean addcomment;
/** Tag for the start of the user code. */
protected String userCodeStartTag = "Start of user code";
/** Tag for the stop of the user code. */
protected String userCodeStopTag = "End of user code";
/** True if isAnnontatedOnly is checked. */
private boolean isAnnotatedOnly;
/** For activity node names. */
private int countLoopStatment;
private int countChoiceStatment;
private String[] sources;
/** use code. */
private List<int[]> userCodeRanges;
/** if set do not visit the node. */
private boolean novisit;
/**
* Constructor.
*
* @param packageObject
* : current package
* @param isAnnotatedOnly
* : if true, process only annotated methods
* @param source
*/
public JavaReverseCUVisitor(Package packageObject, boolean isAnnotatedOnly, String[] source) {
this.packageObject = packageObject;
this.isAnnotatedOnly = isAnnotatedOnly;
this.currentCUName = null;
sources = source;
}
/**
* Extract code ranges inside "user code" tags.
*
* @param node
* @return ranges list
*/
private List<int[]> getRanges(CompilationUnit node) {
ArrayList<int[]> userCodeRange = new ArrayList<int[]>();
int range1 = -1;
int range2 = -1;
List<Comment> listc = node.getCommentList();
for (Comment com : listc) {
if (com instanceof LineComment) {
LineComment linecom = (LineComment)com;
int startpos = linecom.getStartPosition();
int linepos = node.getLineNumber(startpos) - 1;
String text = sources[linepos];
if (text.indexOf(userCodeStartTag) > 0) {
if (range1 < 0) {
range1 = linecom.getStartPosition();
}
}
if (text.indexOf(userCodeStopTag) > 0) {
range2 = linecom.getStartPosition();
int[] range = {range1, range2 };
userCodeRange.add(range);
range1 = -1;
}
}
}
if (range1 > range2) {
range2 = node.getLength() + 1;
int[] range = {range1, range2 };
userCodeRange.add(range);
}
return userCodeRange;
}
// Only for specific reverse (isAnnotatedOnly) : do not parse user code
@Override
public boolean preVisit2(ASTNode node) {
if (userCodeRanges != null) {
// test if user code (then no visit)
int r1 = node.getStartPosition();
int r2 = node.getLength() + r1;
for (int[] pair : userCodeRanges) {
if (r1 > pair[0] && r2 < pair[1]) {
novisit = true;
return false;
}
}
}
// else default behavior : visit if "visit" exists.
return true;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#postVisit(org.eclipse.jdt.core.dom.ASTNode)
*/
@Override
public void postVisit(ASTNode node) {
if (currentActivity == null || novisit) {
novisit = false;
return;
}
// Unprocessed nodes
if (node instanceof Statement && !exitNodeMap.containsKey(node)) {
visitUnknownStat((Statement)node);
}
boolean ok1 = node.getParent() instanceof IfStatement;
boolean ok2 = node.getParent() instanceof Block
&& node.getParent().getParent() instanceof IfStatement;
boolean dolink = true;
if (node instanceof Statement && (ok1 || ok2)) {
IfStatement parent;
if (ok2) {
parent = (IfStatement)node.getParent().getParent();
} else {
parent = (IfStatement)node.getParent();
}
List<Statement> thenstat = new ArrayList<Statement>();
if (isKindofBlock(parent.getThenStatement())) {
thenstat.addAll(((Block)parent.getThenStatement()).statements());
} else if (parent.getThenStatement() != null) {
thenstat.add(parent.getThenStatement());
}
int nthen = 0;
if (thenstat != null) {
nthen = thenstat.size();
}
List<Statement> elsestat = new ArrayList<Statement>();
if (isKindofBlock(parent.getElseStatement())) {
elsestat.addAll(((Block)parent.getElseStatement()).statements());
} else if (parent.getElseStatement() != null) {
elsestat.add(parent.getElseStatement());
}
int nelse = elsestat.size();
// Link if / else branch to correct decision node
if (choiceThenFMap != null && choiceThenFMap.containsKey(parent)
&& choiceThenFMap.get(parent).equals(node)) {
ActivityNode source = innerExitMap.get(choiceThenFMap.get(parent));
ActivityNode target = innerEntryMap.get(node);
if (source != null && target != null) {
ControlFlow flow = createControlFlow(source, target);
flow.setName(source.getName() + "_toElse_" + target.getName());
}
choiceThenFMap.remove(node);
dolink = false;
} else if (choiceElseFMap != null && choiceElseFMap.containsKey(parent)
&& choiceElseFMap.get(parent).equals(node)) {
ActivityNode source = innerExitMap.get(choiceElseFMap.get(parent));
ActivityNode target = innerEntryMap.get(node);
if (source != null && target != null) {
ControlFlow flow = createControlFlow(source, target);
flow.setName(source.getName() + "_toThen_" + target.getName());
}
choiceElseFMap.remove(node);
dolink = false;
} else if (nthen > 0 && thenstat.get(nthen - 1).equals(node)) {
choiceThenMap.put(parent, (Statement)node);
} else if (nelse > 0 && elsestat.get(nelse - 1).equals(node)) {
choiceElseMap.put(parent, (Statement)node);
}
}
// Add comments if exists
ActivityNode act = null;
if (node instanceof Statement && "".equals(nextComment)) {
nextComment = ((Statement)node).getLeadingComment();
if (nextComment == null) {
nextComment = "";
}
act = entryNodeMap.get(node);
}
if (act != null && !"".equals(nextComment)) {
act.createOwnedComment().setBody(nextComment);
addcomment = false;
LogUtils.logCreation(null, null, node, "add comment" + nextComment);
nextComment = "";
}
// link children, remove traces of this being a parent and set this as last statement
if (parentStatements != null && parentStatements.contains(node) && dolink) {
linkToLastChild(node);
parentStatements.remove(node);
// remove this statement's inner nodes from the maps
innerEntryMap.remove(node);
innerExitMap.remove(node);
lastStatement = (Statement)node;
}
} // end of postVisit
/**
* check that statement can be casted into Block.
*
* @param stat
* @return true/false
*/
private boolean isKindofBlock(Statement stat) {
if (stat == null) {
return false;
}
try {
Block b = (Block)stat;
return true;
} catch (ClassCastException e) {
}
return false;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.CompilationUnit)
*/
@Override
public boolean visit(CompilationUnit node) {
if (node == null) {
return false;
}
if (sources != null) {
userCodeRanges = getRanges(node);
}
LogUtils.logEntering(null, null);
currentCUName = node.getJavaElement().getElementName();
LogUtils.logCreation(null, node, null, "Visit " + currentCUName + " for activity");
LogUtils.logExiting();
return true;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration)
*/
@Override
public boolean visit(MethodDeclaration node) {
boolean ret = false;
entryNodeMap = new HashMap<Statement, ActivityNode>();
exitNodeMap = new HashMap<Statement, ActivityNode>();
innerExitMap = new HashMap<Statement, ActivityNode>();
innerEntryMap = new HashMap<Statement, ActivityNode>();
choiceMap = new HashMap<Statement, ActivityNode>();
choiceThenFMap = new HashMap<Statement, Statement>();
choiceElseFMap = new HashMap<Statement, Statement>();
choiceThenMap = new HashMap<Statement, Statement>();
choiceElseMap = new HashMap<Statement, Statement>();
choiceOutMap = new HashMap<Statement, ActivityNode>();
parentStatements = new ArrayList<Statement>();
// add activity to current class
String methodname = node.getName().toString();
TypeDeclaration classnode = null;
String classname = "";
NamedElement specObject = null;
if (node.getParent() instanceof TypeDeclaration) {
classnode = (TypeDeclaration)node.getParent();
classname = classnode.getName().toString();
specObject = packageObject.getOwnedMember(classname);
}
if (specObject != null && specObject instanceof Class) {
List<SingleVariableDeclaration> params = node.parameters();
List<String> paramTypeNames = new ArrayList<String>();
EList<Parameter> pset = null;
for (SingleVariableDeclaration p : params) {
paramTypeNames.add(p.getType().toString());
}
if (paramTypeNames.size() == 0) {
paramTypeNames = null;
}
BehavioralFeature op = ((Class)specObject).getOwnedOperation(methodname, null, null);
// check parameters
if (op != null) {
pset = op.getOwnedParameters();
// parameters names
boolean pnok = ctrlParamTypes(paramTypeNames, pset);
List<Operation> ops2 = ((Class)specObject).getOperations();
List<Operation> ops = new ArrayList<Operation>();
for (Operation oo : ops2) {
if (oo.getName().toString().equals(methodname)) {
ops.add(oo);
}
}
while (!pnok && ops.size() > 1) {
ops.remove(op);
op = ops.get(0);
pset = op.getOwnedParameters();
pnok = ctrlParamTypes(paramTypeNames, pset);
}
if (!pnok) {
op = null;
}
}
if (op == null) {
op = ((Class)specObject).getOwnedReception(methodname, null, null);
if (op != null) {
List<Reception> ops2 = ((Class)specObject).getOwnedReceptions();
List<Reception> ops = new ArrayList<Reception>();
for (Reception oo : ops2) {
if (oo.getName().toString().equals(methodname)) {
ops.add(oo);
}
}
pset = op.getOwnedParameters();
boolean pnok = ctrlParamTypes(paramTypeNames, pset);
while (!pnok && ops.size() > 1) {
ops.remove(op);
op = ops.get(0);
pset = op.getOwnedParameters();
pnok = ctrlParamTypes(paramTypeNames, pset);
}
if (!pnok) {
op = null;
}
}
}
// process only methods annotated @activity
if (op != null && isAnnotatedOnly) {
// list of modifiers and annotations
List<IExtendedModifier> modifiers = node.modifiers();
if (modifiers.isEmpty()) {
op = null;
} else {
// list of annotations names
List<String> flags = new ArrayList<String>();
for (IExtendedModifier flag : modifiers) {
if (flag.isAnnotation()) {
flags.add(flag.toString());
}
}
// check annotation
if (!flags.contains(ANNOTATION_ACTIVITY)) {
op = null;
}
}
}
if (op != null) {
currentActivity = UMLFactory.eINSTANCE.createActivity();
currentActivity.setName(node.getName().toString());
LogUtils.logEntering(currentActivity, "Activity for " + methodname);
currentActivity.setSpecification(op);
((BehavioredClassifier)specObject).getOwnedBehaviors().add(currentActivity);
// parameters type from "op"
for (Parameter paramOp : pset) {
ActivityParameterNode paramu = UMLFactory.eINSTANCE.createActivityParameterNode();
paramu.setName(paramOp.getName());
paramu.setActivity(currentActivity);
paramu.setType(paramOp.getType());
paramu.setParameter(paramOp);
}
initNode = UMLFactory.eINSTANCE.createInitialNode();
initNode.setName("InitNode");
initNode.setActivity(currentActivity);
// create Final node (connections later)
finalNode = UMLFactory.eINSTANCE.createActivityFinalNode();
finalNode.setName("FinalNode");
finalNode.setActivity(currentActivity);
ret = true;
}
}
// ((BehavioredClassifier) packageObject).getOwnedBehaviors().add(currentActivity);
return ret;
}; // end visitMethodDeclaration
// return true = visit all inner nodes
/**
* ctrlParamTypes : check a list of parameter have expected type (control on type name).
*
* @param paramNames
* : type names
* @param pset
* : parameter sets
* @return true/false
*/
private boolean ctrlParamTypes(List<String> paramNames, EList<Parameter> pset) {
boolean pnok = true;
if (paramNames == null) {
return pnok;
}
if (pset == null) {
return false;
}
// last parameter can be a return parameter (no type recorded in that case)
if (pset.size() < paramNames.size() || pset.size() > paramNames.size() + 1) {
return false;
}
for (int ii = 0; ii < paramNames.size(); ii++) {
Parameter pp = pset.get(ii);
if (pp != null || pp.getType() != null) {
String typename = pp.getType().getName();
if (!typename.equals(paramNames.get(ii))) {
pnok = false;
}
}
}
return pnok;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.MethodDeclaration)
*/
@Override
public void endVisit(MethodDeclaration node) {
if (!(lastStatement instanceof ReturnStatement) && currentActivity != null) {
ActivityNode sourceNode = null;
if (lastActivityNode != null) {
sourceNode = lastActivityNode;
lastActivityNode = null;
} else if (lastStatement == null || !exitNodeMap.containsKey(lastStatement)) {
sourceNode = initNode;
} else if (exitNodeMap.containsKey(lastStatement)) {
sourceNode = exitNodeMap.get(lastStatement);
}
createControlFlow(finalNode, sourceNode);
}
LogUtils.logExiting();
currentActivity = null;
}
// Simple Statement case
/**
* call by postVisit for non processed statement => create an OpaqueAction.
*
* @param node
*/
public void visitUnknownStat(Statement node) {
// not applicable to structured nodes
if (node instanceof DoStatement || node instanceof EnhancedForStatement
|| node instanceof BreakStatement || node instanceof SwitchStatement
|| node instanceof SwitchCase || node instanceof Block) {
return;
}
OpaqueAction act = UMLFactory.eINSTANCE.createOpaqueAction();
LogUtils.logCreation(null, null, act, null);
act.setActivity(currentActivity);
act.setName(node.toString().trim());
entryNodeMap.put(node, act);
exitNodeMap.put(node, act);
linkToLastStatement(node);
lastStatement = (Statement)node;
// Java text for Body
act.getLanguages().add("JAVA");
act.getBodies().add(node.toString());
return;
} // end visit visitUnknownStat
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ReturnStatement) TODO : final
* node when no value to return
*/
@Override
public boolean visit(ReturnStatement node) {
if (currentActivity == null) {
return false;
}
// finalNode already exists : created at method declaration
entryNodeMap.put(node, finalNode);
exitNodeMap.put(node, finalNode);
linkToLastStatement(node);
lastStatement = (Statement)node;
return false;
}; // end return statement
// Comment linked to next statement
@Override
public boolean visit(BlockComment node) {
nextComment = nextComment + node.toString();
addcomment = true;
return false;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.LineComment)
*/
@Override
public boolean visit(LineComment node) {
nextComment = nextComment + node.toString();
addcomment = true;
return false;
}
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Javadoc)
*/
@Override
public boolean visit(Javadoc node) {
nextComment = nextComment + node.toString();
addcomment = true;
return false;
}
/*
* (non-Javadoc) While structure creation => nodes creation (merge, decision + Ctrl flow)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.WhileStatement)
*/
@Override
public boolean visit(WhileStatement node) {
if (currentActivity == null) {
return false;
}
// create nodes
MergeNode mergeNode = UMLFactory.eINSTANCE.createMergeNode();
mergeNode.setActivity(currentActivity);
DecisionNode decisionNode = UMLFactory.eINSTANCE.createDecisionNode();
decisionNode.setActivity(currentActivity);
LogUtils.logCreation(null, null, mergeNode, "Create while loop");
// add tags
entryNodeMap.put(node, mergeNode);
exitNodeMap.put(node, decisionNode);
parentStatements.add(node);
innerExitMap.put(node, decisionNode);
innerEntryMap.put(node, mergeNode);
// Add name
String name = node.toString();
name = "While" + "_" + countLoopStatment++;
mergeNode.setName(name + "_merge");
decisionNode.setName(name + "_cond");
// create flow
ControlFlow flow = createControlFlow(decisionNode, mergeNode);
LiteralString stringGuard = UMLFactory.eINSTANCE.createLiteralString();
stringGuard.setValue(node.getExpression().toString());
// flow.setGuard(stringGuard);
flow.setName(name + "_merge_cond");
// control Flow
linkToLastStatement(node);
// set this as the last statement for our children to link to it
lastStatement = (Statement)node;
lastGuard = stringGuard;
// skip the expression statement
node.getBody().accept(this);
return false;
} // end visit WhileStatement
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.IfStatement) Choice "If"
*/
@Override
public boolean visit(IfStatement node) {
if (currentActivity == null) {
return false;
}
// create nodes
DecisionNode decisionNode = UMLFactory.eINSTANCE.createDecisionNode();
decisionNode.setActivity(currentActivity);
MergeNode mergeNode = UMLFactory.eINSTANCE.createMergeNode();
mergeNode.setActivity(currentActivity);
// Add name and log
String name = node.toString();
name = "If" + "_" + countChoiceStatment++;
mergeNode.setName(name + "_merge");
decisionNode.setName(name + "_cond");
LogUtils.logCreation(null, null, decisionNode, "Create if structure");
// add tags
choiceMap.put(node, decisionNode);
choiceOutMap.put(node, mergeNode);
entryNodeMap.put(node, decisionNode);
exitNodeMap.put(node, mergeNode);
parentStatements.add(node);
innerExitMap.put(node, decisionNode);
innerEntryMap.put(node, mergeNode);
// Control Flow
linkToLastStatement(node);
// control flow guard for "then" path
LiteralString stringGuard = UMLFactory.eINSTANCE.createLiteralString();
stringGuard.setValue(node.getExpression().toString());
// then path
if (node.getThenStatement() == null) {
lastGuard = stringGuard;
ControlFlow flow = createControlFlow(mergeNode, decisionNode);
flow.setGuard(stringGuard);
lastGuard = null;
flow.setName(name + "_then");
} else {
choiceThenFMap.put(node, (Statement)node.getThenStatement());
lastGuard = stringGuard;
}
if (node.getElseStatement() == null) {
LiteralString elseGuard = UMLFactory.eINSTANCE.createLiteralString();
elseGuard.setValue("else");
ControlFlow flow = createControlFlow(mergeNode, decisionNode);
flow.setGuard(elseGuard);
flow.setName(name + "_else");
lastGuard = stringGuard;
} else {
// add link decision node first
choiceElseFMap.put(node, (Statement)node.getElseStatement());
}
// set this as the last statement for our children to link to it
lastStatement = (Statement)node;
return true;
} // end visit IfStatement
/**
* End of visit the given type-specific AST node. The default implementation does nothing. Subclasses may
* reimplement.
*
* @param node
* the node to visit
* @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.IfStatement)
*/
@Override
public void endVisit(IfStatement node) {
if (currentActivity == null) {
return;
}
ActivityNode mergeNode = exitNodeMap.get(node);
// connect then path to merge node if needed
if (node.getThenStatement() != null && node.getElseStatement() != null && mergeNode != null) {
Statement lastThenNode = choiceThenMap.get(node);
if (exitNodeMap.containsKey(lastThenNode)) {
ActivityNode act = exitNodeMap.get(lastThenNode);
ControlFlow flow = createControlFlow(mergeNode, act);
if (flow != null) {
flow.setName("thenPathTo_" + mergeNode.getName());
}
}
}
} // end endVisit IfStatement
/**
* Visits the given type-specific AST node. The default implementation does nothing and return true.
* Subclasses may reimplement.
*
* @param node
* the node to visit
* @return true if the children of this node should be visited, and false if the children of this node
* should be skipped
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ForStatement)
*/
@Override
public boolean visit(ForStatement node) {
// for loop
if (currentActivity == null) {
return false;
}
// create nodes
MergeNode mergeNode = UMLFactory.eINSTANCE.createMergeNode();
mergeNode.setActivity(currentActivity);
String name = "For" + "_" + countLoopStatment++;
mergeNode.setName(name + "_merge");
DecisionNode decisionNode = UMLFactory.eINSTANCE.createDecisionNode();
decisionNode.setActivity(currentActivity);
mergeNode.setName(name + "_cond");
LogUtils.logCreation(null, null, mergeNode, "Create for loop");
Variable index = UMLFactory.eINSTANCE.createVariable();
index.setActivityScope(currentActivity);
AddVariableValueAction indexNode = UMLFactory.eINSTANCE.createAddVariableValueAction();
indexNode.setActivity(currentActivity);
// max condition
Expression expr = node.getExpression();
// Assignements
List init = node.initializers();
List<Expression> incr = (List<Expression>)node.updaters();
// get index name
if (init.size() > 0) {
String myName;
if (init.size() > 0) {
myName = init.get(0).toString();
} else {
myName = init.toString();
}
indexNode.setName(myName);
} else {
indexNode.setName("");
}
indexNode.setVariable(index);
// create flows
ControlFlow flow1 = createControlFlow(mergeNode, indexNode);
LiteralString stringGuard = UMLFactory.eINSTANCE.createLiteralString();
flow1.setName(indexNode.getName() + "_merge");
ControlFlow flow = createControlFlow(decisionNode, mergeNode);
if (expr != null) {
stringGuard.setValue(expr.toString());
}
flow.setName(name + "_merge_cond");
// increment
AddVariableValueAction incrNode = UMLFactory.eINSTANCE.createAddVariableValueAction();
incrNode.setActivity(currentActivity);
if (incr.size() > 0) {
incrNode.setName(incr.get(0).toString());
} else {
incrNode.setName("");
}
flow = createControlFlow(mergeNode, incrNode);
flow.setName(incrNode.getName() + "_merge");
// add tags
entryNodeMap.put(node, indexNode);
exitNodeMap.put(node, decisionNode);
parentStatements.add(node);
innerExitMap.put(node, decisionNode);
innerEntryMap.put(node, incrNode);
// Add names and log
mergeNode.setName(name + "_merge");
decisionNode.setName(name + "_cond");
flow.setName(name + "_cond_merge");
// Control Flow
linkToLastStatement(node);
// set this as the last statement for our children to link to it
lastStatement = (Statement)node;
// condition for first inner node
lastGuard = stringGuard;
// skip the expression statement
node.getBody().accept(this);
return false;
} // end visit ForStatement
/**
* visitMethod : to be called when expression is XX = M(...) in place of variable add action.
*
* @param node
* @return CallOperationAction associated to method invocation
*/
public ActivityNode visitMethod(MethodInvocation node) {
CallOperationAction act = UMLFactory.eINSTANCE.createCallOperationAction();
act.setActivity(currentActivity);
act.setName(node.getName().toString());
LogUtils.logCreation(null, null, act, "Create method invocation");
List<?> args = node.arguments();
for (Object arg : args) {
act.createArgument(arg.toString(), null);
}
return act;
} // end MethodInvocation
/*
* (non-Javadoc)
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ExpressionStatement)
*/
@Override
// TODO : add variable declaration / VariableDeclarationFragment
public boolean visit(ExpressionStatement node) {
if (currentActivity == null) {
return false;
}
ActivityNode act;
Expression expr = node.getExpression();
Expression exprvar = null;
Expression exprmeth = null;
if (expr instanceof Assignment) {
exprvar = ((Assignment)expr).getLeftHandSide();
exprmeth = ((Assignment)expr).getRightHandSide();
}
// CallOperation
if (expr instanceof MethodInvocation) {
MethodInvocation callmeth = (MethodInvocation)expr;
act = visitMethod(callmeth);
} else if (expr instanceof Assignment && exprmeth instanceof MethodInvocation) {
MethodInvocation callmeth = (MethodInvocation)exprmeth;
act = visitMethod(callmeth);
((CallOperationAction)act).createResult(exprvar.toString(), null);
} else {
act = UMLFactory.eINSTANCE.createOpaqueAction();
// + body : Java code
((OpaqueAction)act).getLanguages().add("JAVA");
((OpaqueAction)act).getBodies().add(node.toString());
}
act.setActivity(currentActivity);
act.setName(node.toString().trim());
LogUtils.logCreation(null, null, act, "Create expression declaration");
entryNodeMap.put(node, act);
exitNodeMap.put(node, act);
linkToLastStatement(node);
lastStatement = (Statement)node;
return super.visit(node);
} // end visit ExpressionStatement
/*
* (non-Javadoc) Add variable
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ExpressionStatement)
*/
@Override
// FIXME : translate expression
public boolean visit(VariableDeclarationStatement node) {
if (currentActivity == null) {
return false;
}
AddVariableValueAction act = UMLFactory.eINSTANCE.createAddVariableValueAction();
act.setActivity(currentActivity);
// String name = "Action_"+countStatment++;
act.setName(node.toString().trim());
entryNodeMap.put(node, act);
exitNodeMap.put(node, act);
linkToLastStatement(node);
lastStatement = (Statement)node;
LogUtils.logCreation(null, null, act, "Create variable declaration");
// Create variable
Variable var = UMLFactory.eINSTANCE.createVariable();
var.setActivityScope(currentActivity);
act.setVariable(var);
// create variable initialisation
// FIXME Expression expr = node.getDeclaration();
act.setValue(null);
return super.visit(node);
} // end visit VariableDeclarationStatement
/**
* linkToLastChild : look for previous statement and call control flow creation loop only (while, for)0.
*
* @param node
*/
protected void linkToLastChild(ASTNode node) {
ActivityNode targetNode = null;
ActivityNode sourceNode = null;
// If we have an inner entry, link to last statement (child)'s exit
if (innerEntryMap.containsKey(node) && exitNodeMap.containsKey(lastStatement)) {
targetNode = innerEntryMap.get(node);
sourceNode = exitNodeMap.get(lastStatement);
}
// otherwise, create final node and link it to child
createControlFlow(targetNode, sourceNode);
}
/**
* linkToLastStatement : look for last statement and call control flow creation simple statement.
*
* @param node
*/
protected void linkToLastStatement(Statement node) {
ActivityNode targetNode = null;
ActivityNode sourceNode = null;
// If last statement was a parent node, link its inner exit to us.
if (lastStatement != null && parentStatements.contains(lastStatement)) {
// if last statement has a designated exit map, use that
if (innerExitMap.containsKey(lastStatement)) {
sourceNode = innerExitMap.get(lastStatement);
} else {
// TODO Otherwise, we might be inside a region. Add a start point and link to it
// FIXME maybe the parent can create the nodes instead
}
} else {
LiteralString stringuard = UMLFactory.eINSTANCE.createLiteralString();
stringuard.setValue("else");
// Specific case : else statement
// parentStatements does not contains lastStatement any more
if (node.getParent().getNodeType() == ASTNode.IF_STATEMENT) {
IfStatement parent = (IfStatement)node.getParent();
if (choiceMap.containsKey(parent) && parent.getElseStatement() != null
&& parent.getElseStatement().equals(node)) {
sourceNode = choiceMap.get(parent);
choiceMap.remove(parent);
if (lastGuard == null) {
lastGuard = stringuard;
}
}
}
if (node.getParent().getNodeType() == ASTNode.BLOCK
&& node.getParent().getParent().getNodeType() == ASTNode.IF_STATEMENT) {
IfStatement parent = (IfStatement)node.getParent().getParent();
Block block;
if (parent.getElseStatement() instanceof IfStatement) {
AST ast = new AST();
block = (Block)ast.createInstance(ASTNode.BLOCK);
List<Statement> elseblock = new ArrayList<Statement>();
elseblock.add(parent.getElseStatement());
} else {
block = (Block)parent.getElseStatement();
}
if (choiceMap.containsKey(parent) && block != null && block.statements().size() > 0
&& block.statements().get(0).equals(node)) {
sourceNode = choiceMap.get(parent);
choiceMap.remove(parent);
if (lastGuard == null) {
lastGuard = stringuard;
}
}
}
}
// if this has a registered entry node, use it
if (entryNodeMap.containsKey(node)) {
targetNode = entryNodeMap.get(node);
// if we haven't yet found a sourceNode, use the exitMap's or a new one
if (sourceNode == null) {
if (lastActivityNode != null) {
sourceNode = lastActivityNode;
lastActivityNode = null;
} else if (lastStatement == null || !exitNodeMap.containsKey(lastStatement)) {
sourceNode = initNode;
} else if (exitNodeMap.containsKey(lastStatement)) {
sourceNode = exitNodeMap.get(lastStatement);
}
}
}
createControlFlow(targetNode, sourceNode);
}
/**
* createControlFlow : create a control flow from sourceNode to targetNode.
*
* @param targetNode
* @param sourceNode
* @return a ControlFlow
*/
protected ControlFlow createControlFlow(ActivityNode targetNode, ActivityNode sourceNode) {
final int step1 = 30;
final int step2 = 60;
if (sourceNode != null && targetNode != null) {
// no loop on same node nor flow from FinalNode
if (sourceNode == targetNode || sourceNode instanceof FinalNode) {
return null;
}
ControlFlow flow = UMLFactory.eINSTANCE.createControlFlow();
flow.setSource(sourceNode);
flow.setTarget(targetNode);
flow.setActivity(currentActivity);
String name = sourceNode.getName();
if (name.length() > step1) {
name = name.substring(0, step1);
}
name = name + "_" + targetNode.getName();
if (name.length() > step2) {
name = name.substring(0, step2);
}
flow.setName(name);
LogUtils.logCreation(null, null, flow, null);
if (sourceNode instanceof DecisionNode) {
if (lastGuard != null) {
flow.setGuard(lastGuard);
lastGuard = null;
} else {
LiteralString sGuard = UMLFactory.eINSTANCE.createLiteralString();
sGuard.setValue("else");
flow.setGuard(sGuard);
}
}
return flow;
}
return null;
}
/**
* first attempt for a translation AST type / UML type (not written).
*
* @param asttype
* @return UML type
*/
protected Type getUMLType(org.eclipse.jdt.core.dom.Type asttype) {
// ZZZ! datatype or other objects (primitive type, etc.)
// TODO : UML types
// get AST type (simple or primitive type)
String typeName = asttype.toString();
Type umltype = UMLFactory.eINSTANCE.createDataType();
umltype.setName(typeName);
return umltype;
}
}